<?php
// +-------------------------------------------------------------------
// | 
// +-------------------------------------------------------------------
// | Copyright (c) 2009-2016 All rights reserved.
// +-------------------------------------------------------------------
namespace Kcdns\Service\Util\SecureEntry;

class Url
{
    // 使用常量 key 签名 url
    CONST URL_SIGN_TYPE_COMMON = 1;
    
    // 使用会话 key 签名 url
    CONST URL_SIGN_TYPE_SESSION = 2;
    
    // url 签名变量名
    CONST RUL_SIGN_PARAM = '_kt';
    
    // SESSION 会话密钥 key
    CONST SESSION_URL_KEY = 'SECURE_ENTRY_URL_KEY';

    protected static $config = [];

    protected static $route = '';

    protected static $routes = [];

    public static function init ($config, $route)
    {
        self::$config = $config;
        self::$route = $route;
    }

    /**
     * *******************************************************************************************
     * *************************************** 校验 url 签名 ***************************************
     * *******************************************************************************************
     */
    public static function check ()
    {
        // 校验 url 签名
        $sign = self::_getSign($_SERVER['REQUEST_URI'], self::$route);
        if ($sign && (! isset($_GET[self::RUL_SIGN_PARAM]) || $sign !== $_GET[self::RUL_SIGN_PARAM]))
        {
            throw new \Exception('INVALID URL');
        }
        return true;
    }

    /**
     * *******************************************************************************************
     * *************************************** 生成 url 签名 ***************************************
     * *******************************************************************************************
     */
    public static function sign ($url, $route)
    {
        $sign = self::_getSign($url, $route);
        if ($sign)
        {
            $spe = strpos($url, '?') ? '&' : '?';
            $url .= $spe . self::RUL_SIGN_PARAM . '=' . $sign;
        }
        return $url;
    }

    /**
     * 检测入口是否需要 URL 签名
     */
    protected static function _isProtected ($route)
    {
        if (! $route)
        {
            return false;
        }
        
        if (! isset(self::$routes[$route]))
        {
            do
            {
                self::$routes[$route] = false;
                
                // 在url验证白名单内
                is_array(self::$config['SECURE_ENTRY_URL_IGNORE']) or self::$config['SECURE_ENTRY_URL_IGNORE'] = [];
                foreach (self::$config['SECURE_ENTRY_URL_IGNORE'] as $_vv)
                {
                    if (preg_match("#^{$_vv}$#i", $route))
                    {
                        break (2);
                    }
                }
                
                // 默认使用全局密钥
                self::$routes[$route] = self::URL_SIGN_TYPE_COMMON;
                
                // 是否使用独立密钥
                is_array(self::$config['SECURE_ENTRY_URL_PRIVATE']) or self::$config['SECURE_ENTRY_URL_PRIVATE'] = [];
                foreach (self::$config['SECURE_ENTRY_URL_PRIVATE'] as $_vv)
                {
                    if (preg_match("#^{$_vv}$#i", $route))
                    {
                        self::$routes[$route] = self::URL_SIGN_TYPE_SESSION;
                        break (2);
                    }
                }
            }
            while (0);
        }
        
        return self::$routes[$route];
    }

    /**
     * 生成签名
     *
     * @param string $url            
     * @param string $route            
     */
    protected static function _getSign ($url = '', $route)
    {
        $type = self::_isProtected($route);
        $sign = '';
        if ($type)
        {
            // 默认使用全局加密密钥
            $secureKey = self::$config['SECURE_ENTRY_URL_KEY'];
            if ($type == self::URL_SIGN_TYPE_SESSION)
            {
                $_SESSION[self::SESSION_URL_KEY] = $_SESSION[self::SESSION_URL_KEY] ?  : uniqid();
                $secureKey = $_SESSION[self::SESSION_URL_KEY];
            }
            
            $urlParts = parse_url($url);
            $path = $urlParts['path'];
            parse_str($urlParts['query'], $queryArr);
            unset($queryArr[self::RUL_SIGN_PARAM]);
            $query = http_build_query($queryArr);
            $sign = md5($path . '@' . $query . $secureKey);
        }
        return $sign;
    }
}